﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy.Metadata;
using Microsoft.Crm.Sdk.Metadata;

namespace Custom.Crm.General.Utility
{
    public class AttributeMapperUtility
    {
        public class AttributeMapping
        {
            public String FromEntityName;
            public String ToEntityName;
            public AttributeType AttributeType;

            public AttributeMapping(String fromEntityName,
                  String toEntityName,
                  AttributeType typeofAttribute)
            {
                this.FromEntityName = fromEntityName;
                this.ToEntityName = toEntityName;
                this.AttributeType = typeofAttribute;
            }
        }

        private ICrmService m_crmService;
        private IMetadataService m_metaData;

        public AttributeMapperUtility(ICrmService crmService, IMetadataService metaData)
        {
            m_crmService = crmService;
            m_metaData = metaData;
        }

        public List<AttributeMapping> GetAttributeMapping(String fromEntity, String toEntity)
        {
            DynamicEntity entityMap = GetEntityMapping(fromEntity, toEntity);
            List<BusinessEntity> startMap = RetieveAttributeMappingByEntityMap(entityMap);
            List<AttributeMapping> endMap = new List<AttributeMapping>(startMap.Count);

            foreach (DynamicEntity attributeMap in startMap)
            {
                String targetAttribute = attributeMap.Properties["targetattributename"].ToString();
                String sourceAttribute = attributeMap.Properties["sourceattributename"].ToString();
                endMap.Add(new AttributeMapping(sourceAttribute, targetAttribute, GetAttributeType(toEntity, targetAttribute)));
            }

            return endMap;
        }

        private List<BusinessEntity> RetieveAttributeMappingByEntityMap(DynamicEntity entityMap)
        {
            Guid entityMapId = ((Key)entityMap.Properties["entitymapid"]).Value;
            QueryExpression attributeQuery = GetAttributeQuery(entityMapId, new AllColumns());
            return RetrieveMultiple(attributeQuery);
        }

        private AttributeType GetAttributeType(string entityLogicalName, string attributeLogicalName)
        {
            RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest();
            attributeRequest.EntityLogicalName = entityLogicalName;
            attributeRequest.LogicalName = attributeLogicalName;
            RetrieveAttributeResponse attributeResponse = (RetrieveAttributeResponse)m_metaData.Execute(attributeRequest);
            return attributeResponse.AttributeMetadata.AttributeType.Value;
        }

        private List<BusinessEntity> RetrieveMultiple(QueryExpression query)
        {
            RetrieveMultipleRequest request = new RetrieveMultipleRequest();
            request.Query = query;
            request.ReturnDynamicEntities = true;
            RetrieveMultipleResponse response =
             (RetrieveMultipleResponse)m_crmService.Execute(request);
            return response.BusinessEntityCollection.BusinessEntities;
        }

        private DynamicEntity GetEntityMapping(String fromEntity, String toEntity)
        {
            QueryExpression query = new QueryExpression(EntityName.entitymap.ToString());
            query.ColumnSet.AddColumn("entitymapid");
            
            query.Criteria.AddCondition(
             new ConditionExpression("sourceentityname", ConditionOperator.Equal,
             new object[] { fromEntity })
            );
            query.Criteria.AddCondition(
             new ConditionExpression("targetentityname", ConditionOperator.Equal,
             new object[] { toEntity })
            );

            List<BusinessEntity> entityMapping = RetrieveMultiple(query);
            return entityMapping[0] as DynamicEntity;
        }

        private QueryExpression GetAttributeQuery(Guid entityMapId, ColumnSetBase columnSet)
        {
            QueryExpression query = new QueryExpression(EntityName.attributemap.ToString());
            query.ColumnSet = columnSet;
            query.Criteria.AddCondition(new ConditionExpression("entitymapid", ConditionOperator.Equal, entityMapId.ToString()));
            return query;
        }
    }
}